package com.hk.core.sharding;

import com.hk.commons.util.CollectionUtils;
import com.hk.commons.util.SpringContextHolder;
import com.hk.commons.util.StringUtils;
import com.hk.core.jdbc.inspect.SqlInspector;
import lombok.Setter;
import org.apache.shardingsphere.infra.hint.SQLHintPropertiesKey;
import org.apache.shardingsphere.infra.hint.SQLHintTokenType;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;

/**
 * @author Kevin
 * @date 2024-07-03 17:03:20
 */
public abstract class ShardingSqlInspector {

    private HintTokenProvider hintTokenProvider;

    @Setter
    private SqlInspector sqlInspector;

    protected final String internalInspect(String sql) {
        String targetSql = getSqlInspector().inspect(sql);
        HintTokenProvider tokenProvider = getHintTokenProvider();
        HintToken hintToken = tokenProvider.get();
        if (Objects.isNull(hintToken)) {
            return targetSql;
        }
        String token = shardingHintToken(hintToken);
        return StringUtils.hasLength(token) ? token + targetSql : targetSql;
    }

    protected final HintTokenProvider getHintTokenProvider() {
        if (Objects.isNull(hintTokenProvider)) {
            HintTokenProviders tokenProvider = new HintTokenProviders();
            List<HintTokenProvider> providers = new ArrayList<>();
            providers.add(new ThreadLocalHintTokenProvider());
            Map<String, HintTokenProvider> beans = SpringContextHolder.getBeanOfType(HintTokenProvider.class);
            if (CollectionUtils.isNotEmpty(beans)) {
                providers.addAll(CollectionUtils.toList(beans.values()));
            }
            tokenProvider.addProvider(providers);
            this.hintTokenProvider = tokenProvider;
        }
        return hintTokenProvider;
    }

    protected final SqlInspector getSqlInspector() {
        if (Objects.isNull(sqlInspector)) {
            this.sqlInspector = SpringContextHolder.getBeanIfExist(SqlInspector.class)
                    .orElse(SqlInspector.empty());
        }
        return this.sqlInspector;
    }

    protected final String shardingHintToken(HintToken hintToken) {
        String database = hintToken.getDatabase();
        String table = hintToken.getTableName();
        String hintDatabase = null;
        if (StringUtils.hasLength(database)) {
            hintDatabase = STR."\{SQLHintPropertiesKey.SHARDING_DATABASE_VALUE_KEY.getKey()}=\{database}";
        }
        String hintTable = null;
        if (StringUtils.hasLength(table)) {
            hintTable = STR."\{SQLHintPropertiesKey.SHARDING_TABLE_VALUE_KEY.getKey()}=\{table}";
        }
        if (StringUtils.hasLength(hintDatabase) || StringUtils.hasLength(hintTable)) {
            StringBuilder sb = new StringBuilder(SQLHintTokenType.SQL_START_HINT_TOKEN.getKey());
            if (StringUtils.hasLength(hintDatabase)) {
                sb.append(hintDatabase);
            }
            if (StringUtils.hasLength(hintTable)) {
                sb.append(hintTable);
            }
            sb.append("*/");
            return sb.toString();
        }
        return null;
    }
}
